package com.seleniti.enpareja.web.appbase.client.control.controller;

import org.ioc.commons.flowcontrol.common.impl.ReceiverAdapter;
import com.seleniti.enpareja.controller.control.controller.LoginFailedCause;
import org.ioc.commons.flowcontrol.operationmanager.IsOperation;
import org.ioc.commons.flowcontrol.operationmanager.OperationManager;
import org.ioc.commons.flowcontrol.placecontroller.HasPlaceData;
import org.ioc.commons.impl.gwt.client.flowcontrol.logincontroller.GWTLoginControllerImpl;
import org.ioc.commons.integration.common.RequestManager;
import org.ioc.commons.model.globaldata.GlobalDataController;
import org.ioc.commons.utils.FormatterLogger;

import com.seleniti.enpareja.controller.common.GlobalData;
import com.seleniti.enpareja.controller.common.LogInType;
import com.seleniti.enpareja.controller.common.domain.LoggedUserInfo;
import com.seleniti.enpareja.controller.common.domain.UserAgent;
import com.seleniti.enpareja.controller.control.controller.LoginParams;
import com.seleniti.enpareja.controller.control.place.EnParejaPlace;
import com.seleniti.enpareja.controller.model.integration.service.SecurityManagementService;

public class EnParejaLoginController extends
		GWTLoginControllerImpl<LoggedUserInfo, LoginFailedCause, LoginParams, String> {

	private static final FormatterLogger logger = FormatterLogger.getLogger(EnParejaLoginController.class);

	private SecurityManagementService service;

	private GlobalDataController<GlobalData> globalDataController;

	public EnParejaLoginController(SecurityManagementService service,
			GlobalDataController<GlobalData> globalDataController) {
		this.service = service;
		this.globalDataController = globalDataController;
	}

	@Override
	public <O extends Enum<? extends IsOperation>> void tryLogin(
			String loginName,
			String loginPassword,
			LoginParams params,
			final org.ioc.commons.flowcontrol.logincontroller.LoginController.LoginResponse<LoggedUserInfo, LoginFailedCause> loginResponse,
			O operation, OperationManager<O> operationManager) {
		tryLogin(loginName, loginPassword, params, loginResponse, operation, operationManager, false);
	}

	@Override
	public <O extends Enum<? extends IsOperation>> void tryLogin(
			String loginName,
			String loginPassword,
			LoginParams params,
			final org.ioc.commons.flowcontrol.logincontroller.LoginController.LoginResponse<LoggedUserInfo, LoginFailedCause> loginResponse,
			O operation, OperationManager<O> operationManager, boolean storeAsLocalAccountOnLoginOk /* Ignored */) {
		LogInType type = params.getLoginType();
		final EnParejaPlace whenLoginOk = params.getWhenLoginOk();

		switch (type) {
		case GAE:
			UserAgent ua = (UserAgent) this.globalDataController.getGlobalDataValue(GlobalData.UserAgent);

			if (ua == UserAgent.ANDROID_APP) {
				placeController.goTo(EnParejaPlace.GoogleLoginPlace);
			} else {
				HasPlaceData place = null;
				if (whenLoginOk != null) {
					place = this.placeController.getPlaceData(whenLoginOk);
				} else {
					place = this.placeController.getFirstPlace();
				}
				RequestManager<String> rm = this.service.getGoogleLoginExternalUrl(
						this.placeController.getPlaceAsUrl(place)).to(new ReceiverAdapter<String>() {

					@Override
					public void success(String externalLoginUrl) {
						placeController.goTo(EnParejaPlace.ExternalLoginPlace, false, null, externalLoginUrl);
					}
				});

				if (operation != null && operationManager != null) {
					rm.performing(operation, operationManager).caller(this);
				}

			}
			break;
		case UserPassword:

			logger.info("Try login '{0}'...", loginName);

			RequestManager<LoggedUserInfo> rm = this.service.tryLogin(loginName, loginPassword).with(/*
																									 * No
																									 * need
																									 */).caller(this)
					.to(new ReceiverAdapter<LoggedUserInfo>() {

						@Override
						public void success(LoggedUserInfo result) {
							logger.info("... login tried.");
							checkDoUserPasswordLoginResult(result, loginResponse, whenLoginOk);
						}

					});
			if (operation != null && operationManager != null) {
				rm.performing(operation, operationManager).caller(this);
			}
			break;
		}

	}

	protected void checkDoUserPasswordLoginResult(
			LoggedUserInfo result,
			org.ioc.commons.flowcontrol.logincontroller.LoginController.LoginResponse<LoggedUserInfo, LoginFailedCause> loginResponse,
			EnParejaPlace goWhenLoginOk) {
		/**
		 * Set the received login info in the global context
		 */
		this.loggedUserInfo = result;

		if (!result.isLogged()) {
			if (loggedUserInfo.getUserEmail()!=null && !loggedUserInfo.isPasswordSet()) {
				loginResponse.onLoginFailed(LoginFailedCause.NO_PASSWORD_SET, null);
			} else {
				loginResponse.onLoginFailed(LoginFailedCause.USER_OR_PASS_INVALID, null);
			}
		} else {
			loginResponse.onLoginOk(loggedUserInfo);

			if (this.placeController != null) {
				/**
				 * Go back from the place which required a login
				 */
				if (goWhenLoginOk == null) {
					goWhenLoginOk = EnParejaPlace.Main;
				}

				this.placeController.goTo(goWhenLoginOk);
			}
		}

	}

	@Override
	public boolean isLogged() {
		return this.loggedUserInfo != null && this.loggedUserInfo.isLogged();
	}

	@Override
	public <O extends Enum<? extends IsOperation>> void logout(
			final org.ioc.commons.flowcontrol.logincontroller.LoginController.LogoutResponse<String> logoutResponse,
			O operation, OperationManager<O> operationManager) {
		LoggedUserInfo lui = this.getLoggedUserInfo();

		if (lui != null && lui.isRequiredExternalLogoutUrl()) {
			logger.info("Login out externally");
			RequestManager<String> rm = service.getGoogleLogoutExternalUrl(this.placeController
					.getPlaceAsUrl(this.placeController.getFirstPlace()));
			if (operation != null) {
				rm.performing(operation, operationManager);
			}
			rm.to(new ReceiverAdapter<String>() {

				@Override
				public void success(String externalLogoutUrl) {
					if (logoutResponse != null) {
						logoutResponse.onLogout(externalLogoutUrl);
					}
				}
			});
		} else {
			logger.info("Login out internally");

			RequestManager<Void> rm = service.logout();
			if (operation != null) {
				rm.performing(operation, operationManager);
			}
			rm.to(new ReceiverAdapter<Void>() {

				@Override
				public void success(Void result) {
					refreshLoggedUserInfo();
					if (signInView != null) {
						placeController.goTo(signInView);
					}
					if (logoutResponse != null) {
						logoutResponse.onLogout(null);
					}
				}
			});
		}
	}

}
